<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
 * 从Cloudflare获取用户id 
 * 
 * @package CloudflareIP
 * @author yaecho
 * @version 0.0.1
 * @link http://yaecho.net
 */
class CloudflareIP_Plugin implements Typecho_Plugin_Interface
{
    /**
     * 获取cloudflare ipv4地址范围
     */
    const IPV4_URL = 'https://www.cloudflare.com/ips-v4';

    /**
     * 缓存文件
     */
    const IP_FILE = '/ipranges.json';

    /**
     * 缓存时间
     */
    const CACHE_TIME = 1296000;

    /**
     * 激活插件方法,如果激活失败,直接抛出异常
     * 
     * @access public
     * @return void
     * @throws Typecho_Plugin_Exception
     */
    public static function activate()
    {
        //注册前台事件
        Typecho_Plugin::factory('index.php')->begin = array('CloudflareIP_Plugin', 'change');
        //注册后台事件
        Typecho_Plugin::factory('admin/common.php')->begin = array('CloudflareIP_Plugin', 'change');
        //获取ip范围
        try {
            self::getCloudflareIP();
        } catch (\Exception $e) {
            throw new Typecho_Plugin_Exception('Cloudflare IP范围获取出错！');
        }
    }
    
    /**
     * 禁用插件方法,如果禁用失败,直接抛出异常
     * 
     * @static
     * @access public
     * @return void
     * @throws Typecho_Plugin_Exception
     */
    public static function deactivate(){
        //删除缓存文件
        $file = dirname(__FILE__) . self::IP_FILE;
        file_exists($file) && unlink($file);
    }
    
    /**
     * 获取插件配置面板
     * 
     * @access public
     * @param Typecho_Widget_Helper_Form $form 配置面板
     * @return void
     */
    public static function config(Typecho_Widget_Helper_Form $form) {}
    
    /**
     * 个人用户的配置面板
     * 
     * @access public
     * @param Typecho_Widget_Helper_Form $form
     * @return void
     */
    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    
    /**
     * 插件实现方法
     * 
     * @access public
     * @return void
     */
    public static function change()
    {
        //检查缓存文件是否过期
        $file = dirname(__FILE__) . self::IP_FILE;
        if ((time() - filemtime($file)) > self::CACHE_TIME || !file_exists($file)) {
            //更新缓存文件
            register_shutdown_function('CloudflareIP_Plugin::getCloudflareIP');
        }
        //检查是否是来自cloudflare cdn的请求
        if (!self::cloudflareCheckIP($_SERVER['REMOTE_ADDR'])) {
            return;
        }
        //替换ip
        if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
            $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
        }
    }

    /**
     * 检查ip是否来自cloudflare
     *
     * @param string $ip
     * @return bool 
     */
    private static function cloudflareCheckIP($ip) {
        $cf_ips = self::getLocalCloudflareIP();

        if (!is_array($cf_ips) || count($cf_ips) <= 1) {
            return false;
        }

        $is_cf_ip = false;
        foreach ($cf_ips as $cf_ip) {
            if (self::ip_in_range($ip, $cf_ip)) {
                $is_cf_ip = true;
                break;
            }
        }
        return $is_cf_ip;
    }

    /**
     * ip与范围对比
     *
     * @param string $ip
     * @param string $range
     * @return bool
     */
    private static function ip_in_range($ip, $range) {
        if (strpos($range, '/') == false) {
            $range .= '/32';
        }
        
        // $range is in IP/CIDR format eg 127.0.0.1/24
        list($range, $netmask) = explode('/', $range, 2);
        $range_decimal = ip2long($range);
        $ip_decimal = ip2long($ip);
        $wildcard_decimal = pow(2, (32 - $netmask)) - 1;
        $netmask_decimal = ~ $wildcard_decimal;
        return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
    }

    /**
     * 获取远程ip并写入文件
     *
     * @return void
     * @author Yaecho 
     */
    public static function getCloudflareIP()
    {
        $ipRanges = file_get_contents(self::IPV4_URL);
        $ipRangesArray = array_filter(explode("\n", $ipRanges));
        if (is_array($ipRangesArray) && count($ipRangesArray) > 1) {
            file_put_contents(dirname(__FILE__) . self::IP_FILE, json_encode($ipRangesArray));
        } else {
            throw new \Exception('解析错误');
        }
    }

    /**
     * 获取缓存文件
     *
     * @return void
     * @author Yaecho 
     */
    private static function getLocalCloudflareIP()
    {
        return json_decode(file_get_contents(dirname(__FILE__) . self::IP_FILE), true);
    }
}
